#include "ATMOS_MasterServer.h"
#include "ATMOS_NetworkConnectionInfo.h"
#include "ATMOS_Packet.h"
#include "ATMOS_NetworkManager.h"
#include "ATMOS_MasterServerPacketID.h"
#include "ATMOS_DatabaseConnectionInfo.h"
#include "ATMOS_MSClient.h"
#include "ATMOS_MSClientInfo.h"
#include "ATMOS_GameClientInfo.h"
#include "ATMOS_GameServerInfo.h"

#include <iostream>
#include <sstream>
#include <assert.h>

#include "RakNetTypes.h"
#include "RakPeerInterface.h"

#if(ATMOS_PLATFORM == ATMOS_PLATFORM_WINDOWS)
#define WIN32_LEAN_AND_MEAN
#include <conio.h>
#include "windows.h"
#endif

using namespace ATMOS;

MasterServer::MasterServer( NetworkManager *networkManager,
	const DatabaseConnectionInfo &databaseInfo ) :
PacketListener( networkManager )
{
	mDatabase.connect( databaseInfo );

	getNetworkManager()->addPacketListener( this );
	registerHandler( API_MASTER_SERVER, &MasterServer::onAtmosPacket );
}

MasterServer::~MasterServer()
{
	unregisterHandler( API_MASTER_SERVER );
	getNetworkManager()->removePacketListener( this );
}

void MasterServer::onAtmosPacket( Packet *packet )
{
	ATMOS_LOG("MasterServer::onAtmosPacket: " << Packet::idToString(packet->getAtmosPacketID()));
	if( packet->getAtmosPacketID() == API_MASTER_SERVER )
	{
		MasterServerPacketID mspid = (MasterServerPacketID)packet->readUChar();
		switch( mspid )
		{
		case MSPID_NULL:
			break;
		case MSPID_MS_REGISTER_CLIENT:
			onMSRegisterClient( packet );
			break;
		case MSPID_MS_UNREGISTER_CLIENT:
			onMSUnregisterClient( packet );
			break;
		case MSPID_MS_GAME_CONNECTION_ESTABLISHED:
			onMSConnectionEstablished( packet );
			break;
		case MSPID_MS_GAME_CONNECTION_FAILED:
			onMSConnectionFailed( packet );
			break;
		case MSPID_MS_GAME_CONNECTION_REQUEST:
			onMSConnectionRequest( packet );
			break;
		case MSPID_MS_REQUEST_SERVER_LIST:
			onMSRequestServerList( packet );
			break;
		default:
			ATMOS_LOG("unsupported!");
		}
	}
}

int MasterServer::getNumWaitingServers()
{
	return mDatabase.getNumGameServers();
}

int MasterServer::getNumWaitingClients()
{
	return mDatabase.getNumGameClients();
}

GameServerInfoList MasterServer::getWaitingServers( int start, int length )
{
	GameServerInfoList gsil;

	std::list<std::string> serverGUIDList;
	mDatabase.getGameServers( serverGUIDList, start, length );

	for( std::list<std::string>::iterator iter = serverGUIDList.begin();
		iter != serverGUIDList.end();
		++iter )
	{
		GameServerInfo info;

		mDatabase.getGameServerInfo( (*iter), info );

		gsil.push_back( info );
	}

	return gsil;
}

GameClientInfoList MasterServer::getWaitingClients( int start, int length )
{
	GameClientInfoList gcil;

	std::list<std::string> clientGUIDList;
	mDatabase.getGameClients( clientGUIDList, start, length );

	for( std::list<std::string>::iterator iter = clientGUIDList.begin();
		iter != clientGUIDList.end();
		++iter )
	{
		GameClientInfo info;

		mDatabase.getGameClientInfo( (*iter), info );

		gcil.push_back( info );
	}

	return gcil;
}

bool MasterServer::isServer( const std::string &guid )
{
	return mDatabase.isServer( guid );
}

bool MasterServer::isClient( const std::string &guid )
{
	return mDatabase.isClient( guid );
}

GameServerInfo MasterServer::getServerInfo( const std::string &guid )
{
	GameServerInfo info;

	mDatabase.getGameServerInfo( guid, info );

	return info;
}

GameClientInfo MasterServer::getClientInfo( const std::string &guid )
{
	GameClientInfo info;

	mDatabase.getGameClientInfo( guid, info );

	return info;
}

void MasterServer::onMSRegisterClient( Packet *packet )
{
	ATMOS_LOG_FUNC();

	MSClientInfo *info = new MSClientInfo();
	info->name = packet->readString();
	info->server = (packet->readInt() == 1);
	info->port = packet->readInt();
	info->raknetGUID = packet->readString();

	RakNet::RakNetGUID tempGUID;
	tempGUID.FromString( info->raknetGUID.c_str() );

	std::string actualIP =
		getNetworkManager()->getRakNetPeerInterface()->GetSystemAddressFromGuid( tempGUID )
		.ToString( false );

	ATMOS_LOG( "registering MSClient: " << std::endl
		<< info->name << std::endl
		<< info->server << std::endl
		<< "ACTUAL IP: " << actualIP << std::endl
		<< info->port << std::endl
		<< info->raknetGUID);
	ATMOS_LOG("address of packet: " << &packet );

	if( info->server )
	{
		GameServerInfo _info;
		_info.masterServerGUID = info->raknetGUID;
		_info.name = info->name;
		_info.port = info->port;
		_info.ip = actualIP;
		_info.maxPlayers = 32;

		mDatabase.addConnectedGameServer( _info );
	}
	else
	{
		GameClientInfo _info;
		_info.masterServerGUID = info->raknetGUID;
		_info.name = info->name;
		_info.port = info->port;
		_info.ip = actualIP;
		// TODO:
		_info.accountType = 0;

		mDatabase.addConnectedGameClient( _info );
	}
}

void MasterServer::onMSUnregisterClient( Packet *packet )
{
	ATMOS_LOG_FUNC();
	MSClientInfo info;
	info.name = packet->readString();
	info.server = (packet->readInt() == 1);
	info.raknetGUID = packet->readString();

	if( info.server )
	{
		mDatabase.removeConnectedGameServer( info.raknetGUID );
	}
	else
	{
		mDatabase.removeConnectedGameClient( info.raknetGUID );
	}
}

void MasterServer::onMSConnectionRequest( Packet *packet )
{
	ATMOS_LOG_FUNC();

	std::string clientGUID = packet->readString();
	std::string serverGUID = packet->readString();

	ATMOS_LOG("client GUID: " << clientGUID);
	ATMOS_LOG("server GUID: " << serverGUID);

	// notify the client that it can connect to the server
	sendCLGameConnectionApproved( clientGUID, serverGUID );
}

void MasterServer::onMSConnectionEstablished( Packet *packet )
{
	/*
	ATMOS_LOG_FUNC();
	Packet p( API_MASTER_SERVER, clientInfo->raknetGUID );
	p.writeUChar( (unsigned char)MSPID_MS_GAME_CONNECTION_ESTABLISHED );
	getNetworkManager()->sendAtmosPacket( &p );
	*/
}

void MasterServer::onMSConnectionFailed( Packet *packet )
{
	ATMOS_LOG_FUNC();
}

void MasterServer::onMSRequestServerList( Packet *packet )
{
	ATMOS_LOG_FUNC();
	int start = packet->readInt();
	int length = packet->readInt();

	sendCLReceiveServerList( start, length, packet->getRakNetGUID() );
}

void MasterServer::sendCLGameConnectionApproved( const std::string &clientGUID,
	const std::string &serverGUID )
{
	ATMOS_LOG_FUNC();

	GameClientInfo clientInfo;
	mDatabase.getGameClientInfo( clientGUID, clientInfo );

	GameServerInfo serverInfo;
	mDatabase.getGameServerInfo( serverGUID, serverInfo );

	ATMOS_LOG( "client GUID: " << clientGUID << std::endl
		<< "client IP: " << clientInfo.ip << std::endl
		<< "server GUID: " << serverGUID << std::endl
		<< "server IP: " << serverInfo.ip << std::endl);

	Packet p( API_MASTER_SERVER, getNetworkManager()->getLocalGUID() );
	p.writeUChar( (unsigned char)MSPID_CL_GAME_CONNECTION_APPROVED );
	p.writeString( serverInfo.ip );	
	p.writeInt( serverInfo.port );
	p.writeString( clientInfo.ip );
	p.writeInt( clientInfo.port );

	getNetworkManager()->sendPacket( &p, clientGUID );
	getNetworkManager()->sendPacket( &p, serverGUID );
}

void MasterServer::sendCLGameConnectionDenied( const std::string &clientGUID,
	const std::string &serverGUID )
{
	ATMOS_LOG_FUNC();

	GameClientInfo clientInfo;
	mDatabase.getGameClientInfo( clientGUID, clientInfo );

	Packet p( API_MASTER_SERVER, clientGUID );
	p.writeUChar( (unsigned char)MSPID_CL_GAME_CONNECTION_DENIED );
	getNetworkManager()->sendPacket( &p, clientGUID );
}

void MasterServer::sendCLReceiveServerList( int start, int length,
	const std::string &returnGUID )
{
	ATMOS_LOG_FUNC();
	Packet p( API_MASTER_SERVER, returnGUID );
	p.writeUChar( (unsigned char)MSPID_CL_RECEIVE_SERVER_LIST );

	GameServerInfoList gsil = getWaitingServers( start, length );
	p.writeInt( gsil.size() );
	for( GameServerInfoList::iterator iter = gsil.begin();
		iter != gsil.end();
		++iter )
	{
		p.writeString( (*iter).masterServerGUID );
	}

	getNetworkManager()->sendPacket( &p, returnGUID );
}

void MasterServer::onDisconnectionNotification( const std::string &senderGUID )
{
	ATMOS_LOG_FUNC();
}

void MasterServer::onAlreadyConnected( const std::string &senderGUID )
{
	ATMOS_LOG_FUNC();
}

void MasterServer::onIncompatibleProtocolVersion( const std::string &senderGUID )
{
	ATMOS_LOG_FUNC();
}

void MasterServer::onRemoteDisconnectionNotification( const std::string &senderGUID )
{
	ATMOS_LOG_FUNC();
}

void MasterServer::onRemoteConnectionLost( const std::string &senderGUID )
{
	ATMOS_LOG_FUNC();
}

void MasterServer::onNewIncomingConnection( const std::string &senderGUID )
{
	ATMOS_LOG_FUNC();
}

void MasterServer::onConnectionBanned( const std::string &senderGUID )
{
	ATMOS_LOG_FUNC();
}

void MasterServer::onConnectionAttemptFailed( const std::string &senderGUID )
{
	ATMOS_LOG_FUNC();
}

void MasterServer::onNoFreeIncomingConnections( const std::string &senderGUID )
{
	ATMOS_LOG_FUNC();
}

void MasterServer::onInvalidPassword( const std::string &senderGUID )
{
	ATMOS_LOG_FUNC();
}

void MasterServer::onConnectionLost( const std::string &senderGUID )
{
	ATMOS_LOG_FUNC();
}

void MasterServer::onConnectionRequestAccepted( const std::string &senderGUID )
{
	ATMOS_LOG_FUNC();
}

void MasterServer::onUnknownPacket( const std::string &senderGUID )
{
	ATMOS_LOG_FUNC();
}

void MasterServer::removeAllClients()
{
	std::list<std::string> cl;
	mDatabase.getGameClients( cl, 0, 1000 );

	for( std::list<std::string>::const_iterator iter = cl.begin();
		iter != cl.end();
		++iter )
	{
		mDatabase.removeConnectedGameClient( *iter );
	}
}

void MasterServer::removeAllServers()
{
	std::list<std::string> sl;
	mDatabase.getGameServers( sl, 0, 1000 );

	for( std::list<std::string>::const_iterator iter = sl.begin();
		iter != sl.end();
		++iter )
	{
		mDatabase.removeConnectedGameServer( *iter );
	}
}